package com.booleansoft.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

public class RSAHelper {
	private static final int KEYSIZE = 512;

	public static void main(String args[]) {
		// createKey("D:\\key\\public.key","D:\\key\\private.key");

		String liscenceCN = "";

		Key publicKey = getKey("D:\\key\\public.key");
		byte[] enb = encode(publicKey, liscenceCN.getBytes());

		Key privateKey = getKey("D:\\key\\private.key");
		byte[] deb = decode(privateKey, enb);
		System.out.print(new String(deb));
	}

	public static void createKey(String publicpath, String privatePath) {
		KeyPairGenerator pairgen;
		try {
			pairgen = KeyPairGenerator.getInstance("RSA");
			SecureRandom random = new SecureRandom();
			pairgen.initialize(KEYSIZE, random);
			KeyPair keyPair = pairgen.generateKeyPair();
			ObjectOutputStream out = new ObjectOutputStream(
					new FileOutputStream(publicpath));
			out.writeObject(keyPair.getPublic());
			out.close();
			out = new ObjectOutputStream(new FileOutputStream(privatePath));
			out.writeObject(keyPair.getPrivate());
			out.close();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public static Key getKey(InputStream in) {
		ObjectInputStream keyIn = null;
		Key key = null;
		try {
			keyIn = new ObjectInputStream(in);
			key = (Key) keyIn.readObject();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (keyIn != null) {
				try {
					keyIn.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return key;
	}

	public static Key getKey(String path) {
		try {
			return getKey(new FileInputStream(path));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	public static byte[] encode(Key publicKey, byte[] instr) {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		try {
			KeyGenerator keygen = KeyGenerator.getInstance("AES");
			SecureRandom random = new SecureRandom();
			keygen.init(random);
			SecretKey key = keygen.generateKey();
			Cipher cipher = Cipher.getInstance("RSA");
			cipher.init(Cipher.WRAP_MODE, publicKey);
			byte[] wrappedKey = cipher.wrap(key);
			DataOutputStream out = new DataOutputStream(bos);
			out.writeInt(wrappedKey.length);
			out.write(wrappedKey);
			InputStream in = new ByteArrayInputStream(instr);
			cipher = Cipher.getInstance("AES");
			cipher.init(Cipher.ENCRYPT_MODE, key);
			crypt(in, out, cipher);
			in.close();
			out.close();
			bos.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bos.toByteArray();
	}

	public static byte[] decode(Key privateKey, byte[] instr) {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		try {
			DataInputStream in = new DataInputStream(new ByteArrayInputStream(
					instr));
			int length = in.readInt();
			byte[] wrappedKey = new byte[length];
			in.read(wrappedKey, 0, length);
			Cipher cipher = Cipher.getInstance("RSA");
			cipher.init(Cipher.UNWRAP_MODE, privateKey);
			Key key = cipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);
			cipher = Cipher.getInstance("AES");
			cipher.init(Cipher.DECRYPT_MODE, key);
			crypt(in, bos, cipher);
			in.close();
			bos.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
		return bos.toByteArray();
	}

	private static void crypt(InputStream in, OutputStream out, Cipher cipher)
			throws IOException, GeneralSecurityException {
		int blockSize = cipher.getBlockSize();
		int outputSize = cipher.getOutputSize(blockSize);
		byte[] inBytes = new byte[blockSize];
		byte[] outBytes = new byte[outputSize];

		int inLength = 0;
		;
		boolean more = true;
		while (more) {
			inLength = in.read(inBytes);
			if (inLength == blockSize) {
				int outLength = cipher.update(inBytes, 0, blockSize, outBytes);
				out.write(outBytes, 0, outLength);
			} else
				more = false;
		}
		if (inLength > 0)
			outBytes = cipher.doFinal(inBytes, 0, inLength);
		else
			outBytes = cipher.doFinal();
		out.write(outBytes);
	}

}